#include "clientprotocol.h"

#include <sstream>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include "common/serialization.h"

ClientProtocol::ClientProtocol(FakeTcp* socket) : socket(socket) {
	connect(socket, SIGNAL(messageReady()), this, SLOT(readMessage()));
}

ClientProtocol::~ClientProtocol() { }

//True if there is a ready action.
bool ClientProtocol::hasReadyActions() {
	return !(actions.empty());
}

//Remove action from list and return it.
ClientProtocolAction* ClientProtocol::getAction() {
	ClientProtocolAction* action = actions.at(0);
	actions.removeFirst();

	return action;
}

void ClientProtocol::readMessage() {
	while(socket->hasReadyMessage()) {
		//Get code type message
		QString message = socket->getMessage();
		int first = message[0].digitValue();
		message = message.mid(1);

		switch(first) {
			case 1: //Connection message
				parseConnection(message);
				break;
			case 2: //Chat message
				parseChat(message);
				break;
			case 3: //User message
				parseUser(message);
				break;
			case 4: //Friend message
				parseFriend(message);
				break;
		}
	}
}

void ClientProtocol::parseConnection(QString message) {
	//Get message type
	int first = message[0].digitValue();
	message = message.mid(1);

	switch(first) {
		case 3: //SendKeepAlive message(unused)
			ClientConnectionProtocol::answerKeepAlive(socket);
			break;
		case 5: //Connection denied
			actions.push_back(new ClientConnectionAction(CFDeny));
			emit(readyAction());
			break;
		case 6:  { //Connection accepted
			std::istringstream serialStream(message.toStdString());
			boost::archive::text_iarchive serialReader(serialStream);
			Friend* f;
			serialReader >> f;
		
			actions.push_back(new ClientConnectionAction(CFAccept, f));
			emit(readyAction());
			break;
		}
	}
}

void ClientProtocol::parseChat(QString message) {
	//Get message type
	int first = message[0].digitValue();
	message = message.mid(1);
	std::istringstream serialStream(message.toStdString());
	boost::archive::text_iarchive serialReader(serialStream);

	switch(first) {
		case 1: { //Chat started
			Chat* c;
			serialReader >> c;
			actions.push_back(new ClientChatAction(HFStart, c));
			emit(readyAction());
			break;
		}
		case 3: { //Message received
			Chat* c;
			Friend* f;
			QString message;
			serialReader >> c;
			serialReader >> f;
			serialReader >> message;
			actions.push_back(new ClientChatAction(HFSend, c, f, message));
			emit(readyAction());
			break;
		}
		case 4: { //Friend left chat
			Chat* c;
			Friend* f;
			serialReader >> c;
			serialReader >> f;
			actions.push_back(new ClientChatAction(HFExit, c, f));
			emit(readyAction());
			break;
		}
		case 5: { //Chat terminated
			Chat* c;
			serialReader >> c;
			actions.push_back(new ClientChatAction(HFTerminate, c));
			emit(readyAction());
			break;
		}
		case 6: { //Friend entered chat
			Chat* c;
			Friend* f;
			serialReader >> c;
			serialReader >> f;
			actions.push_back(new ClientChatAction(HFEnter, c, f));
			emit(readyAction());
			break;
		}
	}
}

void ClientProtocol::parseUser(QString message) {
	//Get message type
	int first = message[0].digitValue();
	message = message.mid(1);
	std::istringstream serialStream(message.toStdString());
	boost::archive::text_iarchive serialReader(serialStream);

	switch(first) {
		case 1: { //Friend changed status
			Friend* f;
			serialReader >> f;
			actions.push_back(new ClientUserAction(UFFriendStatus, f));
			emit(readyAction());
			break;
		}
		case 2: { //Received friends list
			QList<Friend*> friends;
			serialReader >> friends;
			actions.push_back(new ClientUserAction(UFFriendList, friends));
			emit(readyAction());
			break;
		}
		case 3: { //Changed or not password
			bool isPasswordOk;
			serialReader >> isPasswordOk;
			actions.push_back(new ClientUserAction(UFPassword, isPasswordOk));
			emit(readyAction());
			break;
		}
	}
}

void ClientProtocol::parseFriend(QString message) {
	//Get message type
	int first = message[0].digitValue();
	message = message.mid(1);
	std::istringstream serialStream(message.toStdString());
	boost::archive::text_iarchive serialReader(serialStream);

	switch(first) {
		case 1: { //Person invited to be a friend
			QString login;
			serialReader >> login;
			actions.push_back(new ClientFriendAction(FFInvite, login));
			emit(readyAction());
			break;
		}
		case 3: { //Person denied to be a friend
			QString login;
			serialReader >> login;
			actions.push_back(new ClientFriendAction(FFDeny, login));
			emit(readyAction());
			break;
		}
		case 4: { //Person accepted to be a friend
			Friend* f;
			serialReader >> f;
			actions.push_back(new ClientFriendAction(FFAdd, f));
			emit(readyAction());
			break;
		}
		case 5: { //Friend removed user from list
			Friend* f;
			serialReader >> f;
			actions.push_back(new ClientFriendAction(FFRemove, f));
			emit(readyAction());
			break;
		}
	}
}

/*
 * Client Connection Protocol
 */
void ClientConnectionProtocol::connect(FakeTcp* socket, const QString& login, const QString& password) {
	std::ostringstream serialStream;
	boost::archive::text_oarchive archive(serialStream);
	archive & login;
	archive & password;

	QString serialString(serialStream.str().c_str());
	socket->sendMessage(QString("11") + serialString);
}

void ClientConnectionProtocol::disconnect(FakeTcp* socket) {
	socket->sendMessage("12");
}

void ClientConnectionProtocol::answerKeepAlive(FakeTcp* socket) {
	socket->sendMessage("14");
}

/*
 * Client Chat Protocol
 */

void ClientChatProtocol::start(FakeTcp* socket, int id) {
	std::ostringstream serialStream;
	boost::archive::text_oarchive archive(serialStream);
	archive & id;

	QString serialString(serialStream.str().c_str());
	socket->sendMessage(QString("21") + serialString);
}

void ClientChatProtocol::invite(FakeTcp* socket, Chat* chat, int id) {
	std::ostringstream serialStream;
	boost::archive::text_oarchive archive(serialStream);
	archive & chat;
	archive & id;

	QString serialString(serialStream.str().c_str());
	socket->sendMessage(QString("22") + serialString);
}

void ClientChatProtocol::send(FakeTcp* socket, Chat* chat, QString message) {
	std::ostringstream serialStream;
	boost::archive::text_oarchive archive(serialStream);
	archive & chat;
	archive & message;

	QString serialString(serialStream.str().c_str());
	socket->sendMessage(QString("23") + serialString);
}

void ClientChatProtocol::exit(FakeTcp* socket, Chat* chat) {
	std::ostringstream serialStream;
	boost::archive::text_oarchive archive(serialStream);
	archive & chat;

	QString serialString(serialStream.str().c_str());
	socket->sendMessage(QString("24") + serialString);
}

/*
 * Client User Protocol
 */

void ClientUserProtocol::changePassword(FakeTcp* socket, const QString& oldPassword, const QString& newPassword) {
	std::ostringstream serialStream;
	boost::archive::text_oarchive archive(serialStream);
	archive & oldPassword;
	archive & newPassword;

	QString serialString(serialStream.str().c_str());
	socket->sendMessage(QString("33") + serialString);
}

void ClientUserProtocol::changeStatus(FakeTcp* socket, Status newStatus) {
	std::ostringstream serialStream;
	boost::archive::text_oarchive archive(serialStream);
	archive & newStatus;

	QString serialString(serialStream.str().c_str());
	socket->sendMessage(QString("34") + serialString);
}

void ClientUserProtocol::signup(FakeTcp* socket, const QString& login, const QString& password) {
	std::ostringstream serialStream;
	boost::archive::text_oarchive archive(serialStream);
	archive & login;
	archive & password;

	QString serialString(serialStream.str().c_str());
	socket->sendMessage(QString("35") + serialString);
}

/*
 * Client Friend Protocol
 */
void ClientFriendProtocol::invite(FakeTcp* socket, QString friendLogin) {
	std::ostringstream serialStream;
	boost::archive::text_oarchive archive(serialStream);
	archive & friendLogin;

	QString serialString(serialStream.str().c_str());
	socket->sendMessage(QString("41") + serialString);
}

void ClientFriendProtocol::accept(FakeTcp* socket, QString friendLogin) {
	std::ostringstream serialStream;
	boost::archive::text_oarchive archive(serialStream);
	archive & friendLogin;

	QString serialString(serialStream.str().c_str());
	socket->sendMessage(QString("42") + serialString);
}

void ClientFriendProtocol::deny(FakeTcp* socket, QString friendLogin) {
	std::ostringstream serialStream;
	boost::archive::text_oarchive archive(serialStream);
	archive & friendLogin;

	QString serialString(serialStream.str().c_str());
	socket->sendMessage(QString("43") + serialString);
}

void ClientFriendProtocol::remove(FakeTcp* socket, Friend* f) {
	std::ostringstream serialStream;
	boost::archive::text_oarchive archive(serialStream);
	archive & f;

	QString serialString(serialStream.str().c_str());
	socket->sendMessage(QString("45") + serialString);
}
